import fs from 'fs';
import path from 'path';
import { findCirclePath, type ReferMap } from '../lib/find-circle-path';

type ReferMapInfo = {
  basePath: string;
  referMap: ReferMap;
  // 循环引用路径, 找出所有最小环
  circleReferPath: string[][];
}

const supportFileExts = ['.ts', '.tsx', '.js', '.jsx'];

function scanFiles(dir: string, relativePath = '', files: string[] = []): string[] {
  const entries = fs.readdirSync(dir, { withFileTypes: true });
  
  entries.forEach((entry) => {
    if (entry.isDirectory()) {
      const subPath = path.posix.join(relativePath, entry.name);
      scanFiles(path.join(dir, entry.name), subPath, files);
    } else if (entry.isFile()) {
      const fullPath = path.posix.join(relativePath, entry.name);
      const ext = path.extname(entry.name);
      if (supportFileExts.includes(ext)) {
        files.push(fullPath);
      }
    }
  });
  
  return files;
}

function extractImports(content: string): string[] {
  const imports: string[] = [];
  
  // 匹配 ES6 import 语句，但排除 type-only imports
  const importRegex = /import\s+(?!type\s+)(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"`]([^'"`]+)['"`]/g;
  let match;
  while ((match = importRegex.exec(content)) !== null) {
    imports.push(match[1]);
  }
  
  // 匹配 require 语句
  const requireRegex = /require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
  while ((match = requireRegex.exec(content)) !== null) {
    imports.push(match[1]);
  }
  
  return imports;
}

function resolveRelativePath(fromFile: string, importPath: string, basePath: string): string | null {
  // 忽略 node_modules 和 lib 引用
  if (importPath.startsWith('.') === false || importPath.includes('lib/')) {
    return null;
  }
  
  const fromDir = path.dirname(fromFile);
  const resolved = path.posix.resolve('/', fromDir, importPath);
  let normalizedPath = path.posix.relative('/', resolved);
  
  // 处理椭圆文件扩展名
  if (!path.extname(normalizedPath)) {
    // 尝试添加支持的扩展名
    for (const ext of supportFileExts) {
      const withExt = normalizedPath + ext;
      if (fs.existsSync(path.join(basePath, withExt))) {
        normalizedPath = withExt;
        break;
      }
    }
    
    // 尝试 index 文件
    if (!path.extname(normalizedPath)) {
      for (const ext of supportFileExts) {
        const indexFile = path.posix.join(normalizedPath, 'index' + ext);
        if (fs.existsSync(path.join(basePath, indexFile))) {
          normalizedPath = indexFile;
          break;
        }
      }
    }
  }
  
  return normalizedPath;
}

function normalizeFilePath(filePath: string): string {
  // 保留完整的文件路径，包括扩展名和 index 文件
  return filePath;
}

function toAbsoluteFileUrl(relativePath: string, basePath: string): string {
  const absolutePath = path.resolve(basePath, relativePath);
  // 将 Windows 路径转换为 file:// URL 格式
  const normalizedPath = absolutePath.replace(/\\/g, '/');
  return `file:///${normalizedPath}`;
}

/**
 * 生成引用映射, 分析循环引用情况
 * @param dir 扫描目录
 * @param output 输出文件, 输出映射表的 json 文件
 */
export default function referMap(dir: string, output: string= 'refer-map.json') {
  const info: ReferMapInfo = {
    basePath: dir,
    referMap: {},
    circleReferPath: [],
  }
  const referMap = info.referMap;

  // 扫描所有支持的文件
  const files = scanFiles(dir);
  
  files.forEach(filePath => {
    const fullPath = path.join(dir, filePath);
    const content = fs.readFileSync(fullPath, 'utf-8');
    const imports = extractImports(content);
    
    const normalizedKey = normalizeFilePath(filePath);
    const refers: string[] = [];
    
    imports.forEach(importPath => {
      const resolvedPath = resolveRelativePath(filePath, importPath, dir);
      if (resolvedPath) {
        const normalizedRefer = normalizeFilePath(resolvedPath);
        if (normalizedRefer !== normalizedKey) { // 避免自引用
          refers.push(normalizedRefer);
        }
      }
    });
    
    if (refers.length > 0) {
      referMap[normalizedKey] = [...new Set(refers)]; // 去重
    }
  });

  const circleReferPaths = findCirclePath(referMap);
  // 将循环引用路径转换为绝对 file:// URL
  info.circleReferPath = circleReferPaths.map(cycle => 
    cycle.map(filePath => toAbsoluteFileUrl(filePath, dir))
  );
  fs.writeFileSync(output, JSON.stringify(info, null, 2));
}
